package com.jcommander.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import com.jcommander.controller.DiskListController;
import com.jcommander.controller.FileSystemManager;
import com.jcommander.gui.table.FileSystemTableModel;
import com.jcommander.gui.table.IconLabelRenderer;
import com.jcommander.model.FileSystemNode;
import com.jcommander.model.FileSystemRow;
import com.jcommander.model.IconLabel;

public class SidePaneBuilder {
	private static final int KILO = 1024; //b

	private final JPanel mainSidePanel =  new JPanel(new BorderLayout());
	private final JTable fileSystemTable;
	
	private FileSystemManager manager;
	
	public SidePaneBuilder(FocusListener tableFocusListener) {
		FileSystemTableModel model = new FileSystemTableModel();
		fileSystemTable = new JTable(model);
		manager = new FileSystemManager(fileSystemTable);
		
		fileSystemTable.addFocusListener(tableFocusListener);
	}
	
	public JPanel createMainSidePanePanel() {
		
		mainSidePanel.add(createFileSystemTable(), BorderLayout.CENTER);
		mainSidePanel.add(createTopPanel(), BorderLayout.NORTH);
		mainSidePanel.add(createSummaryPanel(), BorderLayout.SOUTH);
		mainSidePanel.setBorder(BorderFactory.createEtchedBorder());
		return mainSidePanel;
	}
	
	public boolean hasFocus() {
		return fileSystemTable.hasFocus();
	}
	
	public FileSystemManager getFileSystemManager() {
		return manager;
	}
	
	public void clearSelection() {
		fileSystemTable.getSelectionModel().clearSelection();
	}
	
	public void setSelectedRow(int row) {
		fileSystemTable.getSelectionModel().setSelectionInterval(row, row);
	}
	
	public void setFriendManager(FileSystemManager manager) {
		this.manager.setFriendManager(manager);
	}
	
	private Box createTopPanel() {
		Box topBox = Box.createVerticalBox();
		
		topBox.add(createSelectDiskPanel());
		topBox.add(createDisplayPathPanel());
		
		return topBox;
	}
	
	private JPanel createSelectDiskPanel() {
		final NumberFormat numberFormatter = NumberFormat.getInstance(Locale.FRENCH);
		final JLabel diskDescriptor = new JLabel("");
		JPanel diskSelectPanel = new JPanel(new BorderLayout());
		final JComboBox diskList = new JComboBox();
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				final DiskListController diskListController = new DiskListController();
				for (FileSystemNode node : diskListController.getDiskListNode()) {
					diskList.addItem(node);
				}
				diskList.setRenderer(new TextIconComboBoxRenederer());
				diskList.setMaximumRowCount(diskListController.getSize());
				manager.fireDiskListChange(diskList);
				FileSystemNode currentNode = (FileSystemNode)diskList.getItemAt(diskList.getSelectedIndex());
				String label = currentNode.getFileSystemRow().getIconLabel().getLabel();
				StringBuilder text = new StringBuilder();
				text.append("[" + (label.isEmpty() ? "_none_" : label) + "]");
				File currentFile = currentNode.getFile();
				text.append(" " + numberFormatter.format(currentFile.getFreeSpace()/KILO) + " k of ");
				text.append(numberFormatter.format(currentFile.getTotalSpace()/KILO) + " k free");
				diskDescriptor.setText(text.toString());
			}
		});
		
		diskList.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				FileSystemNode selectedDisk = (FileSystemNode)diskList.getSelectedItem();
				String label = selectedDisk.getFileSystemRow().getIconLabel().getLabel();
				StringBuilder text = new StringBuilder();
				text.append("[" + (label.isEmpty() ? "_none_" : label) + "]");
				File selectedFile = selectedDisk.getFile();
				text.append(" " + numberFormatter.format(selectedFile.getFreeSpace()/KILO) + " k of ");
				text.append(numberFormatter.format(selectedFile.getTotalSpace()/KILO) + " k free");
				diskDescriptor.setText(text.toString());
				SwingUtilities.invokeLater(new Runnable() {
					
					public void run() {
						manager.fireDiskListChange(diskList);
					}
				});
			}
		});
		
		JToolBar toolBar = new JToolBar(JToolBar.HORIZONTAL);
		toolBar.setFloatable(false);
		JButton rootButton = new JButton("\\");
		JButton parentDirectory = new JButton("..");
		toolBar.add(rootButton);
		toolBar.add(parentDirectory);
		
		Box buttonBox = Box.createHorizontalBox();
		buttonBox.add(new JSeparator());
		buttonBox.add(toolBar);
		
		diskSelectPanel.add(diskList, BorderLayout.WEST);
		diskSelectPanel.add(diskDescriptor, BorderLayout.CENTER);
		diskSelectPanel.add(buttonBox, BorderLayout.EAST);
		
		return diskSelectPanel;
	}
	
	private JPanel createDisplayPathPanel() {
		JPanel displayPathPanel = new JPanel(new BorderLayout());
		
		JToolBar toolBar = new JToolBar(JToolBar.HORIZONTAL);
		toolBar.setFloatable(false);
		JButton hotListButton = new JButton("*");
		JButton historyButton = new JButton(">");
		toolBar.add(hotListButton);
		toolBar.add(historyButton);
		
		displayPathPanel.add(new JButton(">"), BorderLayout.WEST);
		displayPathPanel.add(new JTextField("Some text"), BorderLayout.CENTER);
		displayPathPanel.add(toolBar, BorderLayout.EAST);
		
		return displayPathPanel;
	}
	
	private JScrollPane createFileSystemTable() {
		
		fileSystemTable.setRowSelectionAllowed(true);
		fileSystemTable.setColumnSelectionAllowed(false);
		fileSystemTable.setFillsViewportHeight(true);
		fileSystemTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		fileSystemTable.setAutoCreateRowSorter(true);
		fileSystemTable.setDefaultRenderer(IconLabel.class, new IconLabelRenderer());
		fileSystemTable.setShowGrid(false);
		
		
		List<CustomTableHeaderRenderer> headerRenderers = new ArrayList<CustomTableHeaderRenderer>(5);
		for (int i = 0; i < 5; i++) {
			headerRenderers.add(new CustomTableHeaderRenderer());
		}
		
		List<TableColumn> headers = Collections.list(
				(Enumeration<TableColumn>)fileSystemTable.getColumnModel().getColumns());
		int i = 0;
		for (TableColumn col : headers) {
			col.setHeaderRenderer(headerRenderers.get(i++));
		}
		
		JTableHeader fileSystemTableHeader = fileSystemTable.getTableHeader();
		fileSystemTableHeader.addMouseListener(new ColumnHeaderListener(headerRenderers));
		
		fileSystemTable.addMouseListener(new MouseAdapter(){
			public void mouseClicked(MouseEvent e){
				if (e.getClickCount() == 2){
			        manager.fireChangeDirectory();
			    }
			}
	     });
		
		JScrollPane scrollPane = new JScrollPane(fileSystemTable);
		return scrollPane;
	}
	
	private Box createSummaryPanel() {
		Box sizeBox = Box.createHorizontalBox();
		
		sizeBox.add(new JLabel("some text / some text / some text"));
		
		return sizeBox;
	}
	
	
	private class ColumnHeaderListener extends MouseAdapter {
	    private SortOrder sortOrder = SortOrder.DESCENDING;
	    private List<CustomTableHeaderRenderer> headerRenderers;
	    
	    public ColumnHeaderListener(List<CustomTableHeaderRenderer> headerRenderers) {
	    	this.headerRenderers = headerRenderers;
	    }
	    
		public void mouseClicked(MouseEvent e) {
			
			JTable table = ((JTableHeader)e.getSource()).getTable();
	        TableColumnModel colModel = table.getColumnModel();
	        
	        
	        // The index of the column whose header was clicked
	        int vColIndex = colModel.getColumnIndexAtX(e.getX());
	        final int mColIndex = table.convertColumnIndexToModel(vColIndex);
	        
	        toogleSortOrder(mColIndex);
	        List <RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
	        switch (mColIndex) {
	        	case 1: sortKeys.add(new RowSorter.SortKey(3, SortOrder.ASCENDING)); //size
	        			sortKeys.add(new RowSorter.SortKey(1, sortOrder));//name
	        		break;
	        	case 2: sortKeys.add(new RowSorter.SortKey(3, SortOrder.ASCENDING)); //size
    					sortKeys.add(new RowSorter.SortKey(2, sortOrder));//name
	        		break;
	        	case 3: sortKeys.add(new RowSorter.SortKey(3, sortOrder)); //size
	        		break;
	        	case 4: sortKeys.add(new RowSorter.SortKey(3, SortOrder.ASCENDING)); //size
	        			sortKeys.add(new RowSorter.SortKey(4, sortOrder)); //date
	        		break;
	        }
	        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
	        sorter.setComparator(3, new Comparator<String>() {

				public int compare(String o1, String o2) {
					String dir = FileSystemRow.DIR_STRING;
					if (o1.equals(dir) || o2.equals(dir)) {
						if (mColIndex == 3) {
							if (sortOrder.equals(SortOrder.ASCENDING)) {
								return -1;
							} else {
								return 1;
							}
						}
						return -1;
					} else {
						if (mColIndex == 3) {
							NumberFormat numberFormatter = NumberFormat.getInstance(Locale.FRENCH);
							
							Long val1 = null;
							Long val2 = null;
							try {
								val1 = numberFormatter.parse(o1).longValue();
								val2 = numberFormatter.parse(o2).longValue();
							} catch (ParseException e) {
								e.printStackTrace();
							}
							if (val1 != null) {
								return val1.compareTo(val2);
							}
						}
						return 0;
					}
				}
			});
	        sorter.setSortKeys(sortKeys);
	        table.setRowSorter(sorter);
	         
	        // Return if not clicked on any column header
	        if (vColIndex == -1) {
	            return;
	        }
	        
	    }
		
		private void toogleSortOrder(int index) {
			if (sortOrder.equals(SortOrder.ASCENDING)) {
				sortOrder = SortOrder.DESCENDING;
			} else {
				sortOrder = SortOrder.ASCENDING;
			}
			for (CustomTableHeaderRenderer cthr : headerRenderers) {
				cthr.setNoneOrderIcon();
			}
			headerRenderers.get(index).setOrderIcon(sortOrder);
		}
	}
	
	private class CustomTableHeaderRenderer extends JLabel implements TableCellRenderer {
	    
		private static final long serialVersionUID = 5964577804330375031L;
		private Icon orderIcon = null;
		
	    public Component getTableCellRendererComponent(JTable table, Object value,
	            boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex) {
	    	// 'value' is column header value of column 'vColIndex'
	        // rowIndex is always -1
	        // isSelected is always false
	        // hasFocus is always false

	        // Configure the component with the specified value
	        setText(value.toString());
	        setIcon(orderIcon);
	        setBorder(UIManager.getBorder("TableHeader.cellBorder"));
	        
	        
	        // Set tool tip if desired
	        setToolTipText((String)value);
	        
	        // Since the renderer is a component, return itself
	        return this;
	    }

	    // The following methods override the defaults for performance reasons
	    public void validate() {}
	    public void revalidate() {}
	    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {}
	    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
	    
	    public void setOrderIcon(SortOrder order) {
	    	if (order.equals(SortOrder.ASCENDING)) {
	    		orderIcon = UIManager.getIcon("Table.ascendingSortIcon");
	    	} else {
	    		orderIcon = UIManager.getIcon("Table.descendingSortIcon");
	    	}
	    }
	    
	    public void setNoneOrderIcon() {
	    	orderIcon = null;
	    }
	}
}
